package com.learning.netease.io.nio;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Iterator;

/**
 * ClassName: NioServer
 * Description: NIO 服务端。直接基于非阻塞的写法,一个线程处理轮询所有请求
 * 1. 用到了非阻塞的API, 再设计上,和BIO可以有很大的不同
 * 2. 问题: 轮询通道的方式,低效,浪费CPU
 * Date: 2021/1/10 22:33 【需求编号】
 *
 * @author Sam Sho
 * @version V1.0.0
 */
@Slf4j
public class NIOServer2 {
    /**
     * 已经建立连接的集合
     */
    private static ArrayList<SocketChannel> channels = new ArrayList<>();

    public static void main(String[] args) throws Exception {
        // 创建网络服务端
        try (ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()) {
            // 设置为非阻塞模式
            serverSocketChannel.configureBlocking(false);
            // 绑定端口
            serverSocketChannel.socket().bind(new InetSocketAddress(8080));
            log.info("启动成功");

            do {
                // 获取新tcp连接通道
                SocketChannel socketChannel = serverSocketChannel.accept();
                // tcp请求 读取/响应
                if (socketChannel != null) {
                    log.info("收到新连接 : {}", socketChannel.getRemoteAddress());
                    // 默认是阻塞的,一定要设置为非阻塞
                    socketChannel.configureBlocking(false);

                    // 接收到连接后，缓存起来
                    channels.add(socketChannel);
                } else {
                    // 没有新连接的情况下,就去处理现有连接的数据,处理完的就删除掉
                    Iterator<SocketChannel> iterator = channels.iterator();
                    if (iterator.hasNext()) {
                        do {
                            SocketChannel ch = iterator.next();
                            try {
                                ByteBuffer requestBuffer = ByteBuffer.allocate(1024);

                                if (ch.read(requestBuffer) == 0) {
                                    // 等于0,代表这个通道没有数据需要处理,那就待会再处理
                                    continue;
                                }
                                while (ch.isOpen() && ch.read(requestBuffer) != -1) {
                                    // 长连接情况下,需要手动判断数据有没有读取结束 (此处做一个简单的判断: 超过0字节就认为请求结束了)
                                    if (requestBuffer.position() > 0) {
                                        break;
                                    }
                                }
                                if (requestBuffer.position() == 0) {
                                    // 如果没数据了, 则不继续后面的处理
                                    continue;
                                }
                                requestBuffer.flip();
                                byte[] content = new byte[requestBuffer.limit()];
                                requestBuffer.get(content);
                                log.info("收到数据,来自：{}. {}", ch.getRemoteAddress(), new String(content));

                                // 响应结果 200
                                String response = "HTTP/1.1 200 OK\r\n" +
                                        "Content-Length: 11\r\n\r\n" +
                                        "Hello World";
                                ByteBuffer buffer = ByteBuffer.wrap(response.getBytes());
                                while (buffer.hasRemaining()) {
                                    ch.write(buffer);
                                }
                                iterator.remove();
                            } catch (IOException e) {
                                e.printStackTrace();
                                iterator.remove();
                            }
                        } while (iterator.hasNext());
                    }
                }
            } while (true);
        }
        // 用到了非阻塞的API, 再设计上,和BIO可以有很大的不同
        // 问题: 轮询通道的方式,低效,浪费CPU
    }
}
